package org.bookie.web.pages.admin;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.tapestry.annotations.InjectComponent;
import org.apache.tapestry.annotations.InjectObject;
import org.apache.tapestry.annotations.Persist;
import org.apache.tapestry.dojo.html.Dialog;
import org.bookie.framework.CommonBasePage;
import org.bookie.model.Category;
import org.bookie.model.Role;
import org.bookie.model.security.Access;
import org.bookie.model.services.CategoryDAO;

/**
 * Page for {@link Category} administration. It allows the creation and update of categories. 
 * 
 * @author Igor Drobiazko
 *
 */
@Access(role=Role.ADMIN)
public abstract class CategoryAdmin extends CommonBasePage{

	private static final Log log = LogFactory.getLog(CategoryAdmin.class);
	
	/**Client identifier of the 'edit category dialog'*/
	public static final String UPDATE_DIALOG = "updateDialog";
	
	/**Client identifier of the 'save new category dialog'*/
	public static final String CREATE_DIALOG = "createDialog";
	
	/**Client identifier of the 'delete category dialog'*/
	public static final String DELETE_DIALOG = "deleteDialog";
	
	/**Client identifier of the category list*/
	public static final String CATEGORY_LIST = "categoryList";
	
	/**Client identifier of the div containing category creation form*/
	public static final String NEW_CATEGORY_DIV = "newCategoryDiv";

	/**
	 * Injected Category service.
	 * @return instance of CategoryDAO
	 */
	@InjectObject("service:bookie.model.CategoryDAO")
	public abstract CategoryDAO getCategoryDAO();
	
	@Persist
	public abstract Long getSelectedCategoryId(); 
	public abstract void setSelectedCategoryId(Long id);
	
	/**Injected update dialog*/
	@InjectComponent(UPDATE_DIALOG)
	public abstract Dialog getUpdateDialog();
	/**Injected create dialog*/
	@InjectComponent(CREATE_DIALOG)
	public abstract Dialog getCreateDialog();
	/**Injected delete dialog*/
	@InjectComponent(DELETE_DIALOG)
	public abstract Dialog getDeleteDialog();
	
	
	/**Get the name of the new {@link Category}*/
	@Persist("client")
	public abstract String getName();
	/**
	 * Set the name of the new {@link Category}
	 * @param name name of the new category
	 */
	public abstract void setName(String name);
	
	/**
	 * Listener method to show a dialog to confirm an update action.
	 * It is invoked when the user click on a {@link Category} link.
	 * @param id identifier of the selected category
	 */
	public void onUpdate(Long id){
		Category category = getCategoryDAO().findById(id);
		if(category==null)return;
		
		setSelectedCategoryId(category.getId());
		setMessage(null);
		showDialog(getUpdateDialog());
	}
	
	/**
	 * Listener method to update the currently selected category.
	 */
	public void update(){
		if(getDelegate().getHasErrors()){
			showDialog(getUpdateDialog());
			return;
		}
		Category category = getCategoryDAO().findById(getSelectedCategoryId());
		if(!isCategoryUnique(category.getName())){
			showDialog(getUpdateDialog());
			return;
		}
		getCategoryDAO().merge(category);
		setSelectedCategoryId(null);
		setMessage(getMessages().getMessage("admin.categoryadmin.update.success.message"));
		hideDialog(getUpdateDialog());
		updateComponents(NEW_CATEGORY_DIV, CATEGORY_LIST);
	}
	
	/**
	 * Listener method to show a dialog to confirm a create action.
	 * It is invoked when the fills the form and submits it.
	 */
	public void onCreate(){
		if(getDelegate().getHasErrors()){
			updateComponents(NEW_CATEGORY_DIV);
			return;
		}
		if(!isCategoryUnique(getName())){
			updateComponents(NEW_CATEGORY_DIV);
			return;
		}
		showDialog(getCreateDialog());
	}
	/**
	 * Listener method to save a new category
	 */
	public void create(){
		getCategoryDAO().makePersistent(new Category(getName()));
		setName(null);
		setMessage(getMessages().getMessage("admin.categoryadmin.create.success.message"));
		hideDialog(getCreateDialog());
		updateComponents(CATEGORY_LIST, NEW_CATEGORY_DIV);
	}
	
	/**
	 * Listener method to cancel the confirmation 
	 */
	public void cancelCreate(){
		hideDialog(getCreateDialog());
	}
	
	/**
	 * Listener method to show a dialog to confirm a delete action.
	 * It is invoked when the user click on delete link.
	 * @param id identifier of the selected category
	 */
	public void onDelete(Long id){
		Category selected = getCategoryDAO().findById(id);
		if (selected == null) {
			log.error("Can't find category with ID=" + id);
			return;
		}
		setSelectedCategoryId(selected.getId());
		showDialog(getDeleteDialog());
	}
	/**
	 * Listener method that deletes category
	 */
	public void delete() {
		Category selected = getCategoryDAO().findById(getSelectedCategoryId());
		getCategoryDAO().makeTransient(selected);
		setSelectedCategoryId(null);
		setMessage(getMessages().format("admin.categoryadmin.delete.message", selected.getName()));
		hideDialog(getDeleteDialog());
		updateComponents(CATEGORY_LIST, NEW_CATEGORY_DIV);
	}
	
	/**
	 * Validates the uniqueness of the category name
	 * @param name name to check the uniqueness
	 * @return <code>true</code> if the given name is unique, <code>false</code> otherwise
	 */
	private boolean isCategoryUnique(String name){
		if(getCategoryDAO().findByName(name)!=null){
			recordError("admin.categoryadmin.create.category.exists");
			return false;
		}
		return true;
	}
	
	/**
	 * Shows the specified dialog and updates it in dynamic response. 
	 * @param dialog dialog to show
	 */
	private void showDialog(Dialog dialog){
		dialog.show();
		updateComponents(dialog.getId());
	}
	
	/**
	 * Hides the specified dialog and updates it in dynamic response. 
	 * @param dialog dialog to hide
	 */
	private void hideDialog(Dialog dialog){
		dialog.hide();
		updateComponents(dialog.getId());
	}
	
	/**
	 * Returns selected category restored from the ID stored in session
	 * @return
	 */
	public Category getSelectedCategory() {
		if (getSelectedCategoryId() != null) {
			return getCategoryDAO().findById(getSelectedCategoryId());
		}
		return null;
	}
}
